/*
 * DevicesIIC.c
 *
 *      Author: Honrun
 */
#include "CH58x_common.h"
#include "stdint.h"
#include "DevicesDelay.h"
#include "DevicesIIC.h"


#define IIC_SPEED_VALUE     100000
#define IIC_DELAY_TIME      ((1000000.0f / IIC_SPEED_VALUE) + 1.0f)


void vIIC0Init(void)
{
    GPIOPinRemap(ENABLE, RB_PIN_I2C);

    GPIOB_ModeCfg(GPIO_Pin_20 | GPIO_Pin_21, GPIO_ModeIN_PU);

    I2C_Init(I2C_Mode_I2C, IIC_SPEED_VALUE, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit, 0x10);
}

/*!
    \brief      reset i2c bus
    \param[in]  none
    \param[out] none
    \retval     none
*/
void vIIC0DeInit(void)
{
    int8_t i = 0;

    GPIOA_ModeCfg(GPIO_Pin_20 | GPIO_Pin_21, GPIO_ModeOut_PP_5mA);

    vDelayUs(100);

    /* 发送9个时钟脉冲，以解除死锁 */
    for(i = 0; i < 9; ++i)
    {
        vDelayUs(5);
        GPIOA_ResetBits(GPIO_Pin_21);
        vDelayUs(5);
        GPIOA_ResetBits(GPIO_Pin_20);
        vDelayUs(5);
        GPIOA_SetBits(GPIO_Pin_21);
        vDelayUs(5);
        GPIOA_SetBits(GPIO_Pin_20);
    }

    vIIC0Init();
}

int8_t cI2CxWriteDatas(uint32_t uiI2C, uint16_t usIDAddr, uint16_t usRegAddr, void* pvDatas, int32_t iLength)
{
    void (*vI2CxBusReset)(void) = vIIC0DeInit;
    uint8_t *pucDatas = pvDatas;
    uint32_t timeout = 0, startCnt = 0;

    if((iLength < 1) || (pvDatas == NULL))
        return -1;

    /* i2c master sends start signal only when the bus is idle */
    for(timeout = 0; (timeout <= I2C_BUSY_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET); ++timeout)
    {
        if(timeout >= I2C_BUSY_TIME_OUT)
        {
            vI2CxBusReset();
            return -2;
        }

        vDelayUs(IIC_DELAY_TIME);
    }

    /* 重试 N 次 */
    for(startCnt = 0; startCnt <= I2C_START_OUT; ++startCnt)
    {
        if(startCnt >= I2C_START_OUT)
        {
            vI2CxBusReset();
            return -3;
        }

        I2C_GenerateSTART(ENABLE);
        for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_SB) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        /* send driver address */
        I2C_Send7bitAddress(usIDAddr, I2C_Direction_Transmitter);
        for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_ADDR) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        if(timeout < I2C_BYTE_WRITE_TIME_OUT)
            break;

        vDelayUs(100.0f);
    }

    /* 执行后续操作前，必须清除此状态位 */
    I2C_ClearFlag(I2C_FLAG_ADDR);

    /* send reg address */
    I2C_SendData(usRegAddr);

    while((iLength--) > 0)
    {
        /* wait until the transmit data buffer is empty */
        for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_TXE) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        /* send data */
        I2C_SendData(*pucDatas++);
    }

    /* wait until the byte transmission finishes */
    for(timeout = 0; (timeout < (I2C_BYTE_WRITE_TIME_OUT * 2)) && (I2C_GetFlagStatus(I2C_FLAG_BTF) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    /* send a stop condition to I2C bus */
    I2C_GenerateSTOP(ENABLE);
    for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && ((R16_I2C_CTRL1 & 0x0200) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    return 0;
}

int8_t cI2CxReadDatas(uint32_t uiI2C, uint16_t usIDAddr, uint16_t usRegAddr, void* pvDatas, int32_t iLength)
{
    void (*vI2CxBusReset)(void) = vIIC0DeInit;
    uint8_t *pucDatas = pvDatas;
    uint32_t timeout = 0, startCnt = 0;

    if((iLength < 1) || (pvDatas == NULL))
        return -1;

    /* i2c master sends start signal only when the bus is idle */
    for(timeout = 0; (timeout <= I2C_BUSY_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET); ++timeout)
    {
        if(timeout >= I2C_BUSY_TIME_OUT)
        {
            vI2CxBusReset();
            return -2;
        }

        vDelayUs(IIC_DELAY_TIME);
    }

    /* 重试 N 次 */
    for(startCnt = 0; startCnt <= I2C_START_OUT; ++startCnt)
    {
        if(startCnt >= I2C_START_OUT)
        {
            vI2CxBusReset();
            return -3;
        }

        I2C_GenerateSTART(ENABLE);
        for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_SB) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        /* send driver address */
        I2C_Send7bitAddress(usIDAddr, I2C_Direction_Transmitter);
        for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_ADDR) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        if(timeout < I2C_BYTE_WRITE_TIME_OUT)
            break;

        vDelayUs(100.0f);
    }

    /* 执行后续操作前，必须清除此状态位 */
    I2C_ClearFlag(I2C_FLAG_ADDR);

    /* send reg address */
    I2C_SendData(usRegAddr);
    for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_BTF) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    /* 用户手册：必须在START置1前设定 */
    R16_I2C_CTRL1 = (iLength == 2) ? (R16_I2C_CTRL1 | 0x0800) : (R16_I2C_CTRL1 & ~0x0800);

    I2C_GenerateSTART(ENABLE);
    for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_SB) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    /* send driver address */
    I2C_Send7bitAddress(usIDAddr, I2C_Direction_Receiver);
    for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_ADDR) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    /* 如果小于3字节，就需要在清除 I2C_FLAG_ADDR 前就设置为NAK */
    I2C_AcknowledgeConfig((iLength < 3) ? DISABLE : ENABLE);

    /* 清除此bit后，就会立即启动IIC移位寄存器进行数据读取 */
    I2C_ClearFlag(I2C_FLAG_ADDR);

    while((--iLength) > 0)
    {
        /* 等待 BTC bit 置位，以阻止 IIC 在准确设置 ACK/NACK 状态前进行移位 */
        for(timeout = 0; (timeout < (I2C_BYTE_READ_TIME_OUT * 2)) && (I2C_GetFlagStatus(I2C_FLAG_BTF) == RESET); ++timeout)
            vDelayUs(IIC_DELAY_TIME);

        I2C_AcknowledgeConfig((iLength < 3) ? DISABLE : ENABLE);

        /* read data */
        *pucDatas++ = I2C_ReceiveData();
    }

    /* wait until RBNE bit is set */
    for(timeout = 0; (timeout < I2C_BYTE_READ_TIME_OUT) && (I2C_GetFlagStatus(I2C_FLAG_RXNE) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    /* read data */
    *pucDatas++ = I2C_ReceiveData();

    /* send a stop condition to I2C bus */
    I2C_GenerateSTOP(ENABLE);
    for(timeout = 0; (timeout < I2C_BYTE_WRITE_TIME_OUT) && ((R16_I2C_CTRL1 & 0x0200) == RESET); ++timeout)
        vDelayUs(IIC_DELAY_TIME);

    return 0;
}
